package net.xdclass.xdclass_shiro.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro 配置
 *
 * @author zhangsj
 * @date 2020/5/2 15:29
 */
@Configuration
@Slf4j
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
        log.info("ShiroFilterFactoryBean.shiroFilter()");

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 必须设置securityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager());

        // 需要登录的接口，如果访问某个接口，需要登录却没有登录，
        // 则需要调用此接口（如果不是前后端分离，则跳转页面）
        shiroFilterFactoryBean.setLoginUrl("/pub/need_login");

        // 登录成功跳转URL，如果前后端分离，则没有这个调用
        shiroFilterFactoryBean.setSuccessUrl("/");

        // 没有权限，未授权，调用该接口，先验证是否登录 -》 再验证是否有权限
        shiroFilterFactoryBean.setUnauthorizedUrl("/pub/not_permit");

        // 拦截器路径，部分路径无法进行拦截，时有时无：
        // 因为使用的是hashMap 无序的，需要修改为有序的linkedHashMap
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 退出过滤器
        filterChainDefinitionMap.put("/logout", "logout");
        // 匿名可以访问，也就是游客模式
        filterChainDefinitionMap.put("/pub/**", "anon");
        // 登录用户才可以访问
        filterChainDefinitionMap.put("/authc/**", "authc");
        // 管理员角色才可以访问
        filterChainDefinitionMap.put("/admin/**", "roles[admin]");
        // 有编辑权限的才可以访问
        filterChainDefinitionMap.put("/video/**", "perms[video_update]");

        // authc：url必须通过认证才可访问
        // anon：url可以通过匿名访问
        // 该配置应该放在linkedHashMap最后一个，防止出现时有时无的情况
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setSessionManager(sessionManager());

        // 设置realm 放在最后，不然某些情况会不生效
        defaultSecurityManager.setRealm(customRealm());
        return defaultSecurityManager;
    }

    @Bean
    public CustomRealm customRealm() {
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return customRealm;
    }

    /**
     * 添加 MD5 加密
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();

        // 设置散列算法：这里使用 MD5 算法
        credentialsMatcher.setHashAlgorithmName("md5");
        // 散列次数，好比散列2次，相当于md5(md5(xxx))
        credentialsMatcher.setHashIterations(2);

        return credentialsMatcher;
    }

    /**
     * 自定义 sessionManager
     */
    @Bean
    public SessionManager sessionManager() {
        CustomSessionManager customSessionManager = new CustomSessionManager();

        // 超时时间，默认 30 分钟，会话超时；方法里面的是毫秒 20秒
        customSessionManager.setGlobalSessionTimeout(20 * 1000);
        return customSessionManager;
    }
}
